<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript烟花特效</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        const explodedNumber = 66;

        class Particle {
            constructor(x, y, color, size) {
                this.x = x;
                this.y = y;
                this.color = color;
                this.size = size;
                this.speedX = (Math.random() * 4 - 2);
                this.speedY = (Math.random() * 4 - 2);
                this.gravity = 0.05;
                this.alpha = 1;
            }
            update(fireworkSpeedX = 0) {
                this.speedY += this.gravity;
                this.x += this.speedX + fireworkSpeedX / 2;
                this.y += this.speedY;
                this.alpha -= 0.01;
            }
            draw() {
                ctx.save();
                ctx.globalAlpha = this.alpha;
                ctx.fillStyle = this.color;
                // ctx.beginPath();
                // ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                // ctx.fill();
                ctx.beginPath();
                ctx.moveTo(this.x, this.y);
                for (let t = 0; t < 2 * Math.PI; t += 0.01) {
                    const x = this.x + 16 * Math.pow(Math.sin(t), 3) * this.size / 6;
                    const y = this.y - (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)) * this.size / 6;
                    ctx.lineTo(x, y);
                }
                ctx.closePath();
                ctx.fill();
                ctx.restore();
            }
        }

        class Firework {
            constructor(x, y) {
                this.x = x;
                this.y = y;
                this.speedX = (Math.random() * 2 - 1);
                this.particles = [];
                this.exploded = false;
                this.size = Math.floor(Math.random() * 5 + 5);
                this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
            }
            update() {
                if (!this.exploded) {
                    this.y -= 4;
                    this.x += this.speedX;
                    if (this.y <= 200) {
                        this.exploded = true;
                        this.createParticles();
                    }
                } else {
                    for (let particle of this.particles) {
                        particle.update(this.speedX);
                    }
                }
            }
            draw() {
                if (!this.exploded) {
                    ctx.fillStyle = this.color;
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                    ctx.fill();
                } else {
                    for (let particle of this.particles) {
                        particle.draw();
                    }
                }
            }
            createParticles() {
                for (let i = 0; i < explodedNumber; i++) {
                    this.particles.push(new Particle(this.x, this.y, this.color, Math.floor(Math.random() * 5 + 1)));
                }
            }
        }

        const fireworks = [];
        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            for (let firework of fireworks) {
                firework.update();
                firework.draw();
            }
            requestAnimationFrame(animate);
        }

        canvas.addEventListener('click', (event) => {
            const firework = new Firework(event.clientX, canvas.height);
            fireworks.push(firework);
        });

        animate();
    </script>
</body>
</html>
